﻿
#region 文件信息
/*----------------------------------------------------------------
// 
// 文件名称：	
// 文件功能描述：	
// 设计要求：	
//
// 文 件 名：    GeneratorDTO
// 创建者：      杨程
// 创建日期：	    2023/2/21 12:26:30

//----------------------------------------------------------------*/
#endregion

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Vampirewal.Admin.SourceGenerator.Tools;

namespace Vampirewal.Admin.SourceGenerator.Generator;

public class DtoSyntaxReceiver : ISyntaxReceiver
{
    public List<AttributeSyntax> CandidateClasses { get; } = new List<AttributeSyntax>();

    public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
    {
        //if ((syntaxNode is ClassDeclarationSyntax cds)&&cds.AttributeLists.Count>0&& cds.Identifier.Value is IdentifierNameSyntax identifierName)
        //{

        //    CandidateClasses.Add(cds);
        //}

        if (syntaxNode is AttributeSyntax cds && cds.Name is IdentifierNameSyntax identifierName && identifierName.Identifier.ValueText == "GeneratorDTO")
        {
            CandidateClasses.Add(cds);
        }
    }
}

/// <summary>
/// 
/// </summary>
[Generator]
public partial class GeneratorDTO : ISourceGenerator
{
    public void Execute(GeneratorExecutionContext context)
    {
        var syntaxReceiver = (DtoSyntaxReceiver)context.SyntaxReceiver;
        var attributeSyntaxList = syntaxReceiver.CandidateClasses;

        if (attributeSyntaxList.Count == 0)
        {
            return;
        }

        List<string> ClassName = new List<string>();

        foreach (AttributeSyntax attributeSyntax in attributeSyntaxList)
        {
            // 找到class，并且判断一下是否有parital字段
            var classDeclarationSyntax = attributeSyntax.FirstAncestorOrSelf<ClassDeclarationSyntax>();
            if (classDeclarationSyntax == null || !classDeclarationSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
            {
                throw new Exception("该类不是Partial");
                //continue;
            }

            // 找到namespace
            var namespaceDeclarationSyntax = classDeclarationSyntax.FirstAncestorOrSelf<BaseNamespaceDeclarationSyntax>();
            if (ClassName.Contains(classDeclarationSyntax.Identifier.ValueText))
            {
                continue;
            }

            // 找到property
            var propertyDeclarationList = classDeclarationSyntax.Members.OfType<PropertyDeclarationSyntax>().ToList();
            if (propertyDeclarationList.Count == 0)
            {
                continue;
            }


            StringBuilder sb = new StringBuilder();

            sb.AppendLine("// <auto-generated/>");
            sb.AppendLine("#pragma warning disable");
            sb.AppendLine("#nullable enable");
            sb.AppendLine();

            SyntaxUtils.GetUsings(classDeclarationSyntax).ForEach(f => sb.AppendLine(f));

            var ClassZhuShi = classDeclarationSyntax.GetLeadingTrivia().Where(w => w.IsKind(SyntaxKind.SingleLineCommentTrivia)).ToList();
            string classzs = string.Empty;
            if (ClassZhuShi.Count > 0)
                classzs = ClassZhuShi[1].ToString();


            sb.AppendLine();
            sb.AppendLine($"namespace {namespaceDeclarationSyntax.Name.ToString()}");
            sb.AppendLine("{");
            sb.AppendLine($"    /// <summary>");
            sb.AppendLine($"    /// {classzs}{classDeclarationSyntax.Identifier.Text}的DTO类");
            sb.AppendLine($"    /// </summary>");
            sb.AppendLine($"    public partial class {classDeclarationSyntax.Identifier.Text}DTO");
            sb.AppendLine("    {");

            List<string> PropNames = new List<string>();
            #region 生成DTO属性
            foreach (PropertyDeclarationSyntax prop in propertyDeclarationList)
            {
                //再判断一下属性上是否标记了DtoIgnore特性,没有的话继续，有的话，就不继续了
                if (SyntaxUtils.HasAttribute(prop, "DtoIgnore"))
                {
                    continue;
                }

                var type = prop.Type.ToString();

                var name = prop.Identifier.Text;

                sb.AppendLine();

                #region 获取注释

                var Leads = prop.GetLeadingTrivia().Where(w => w.IsKind(SyntaxKind.SingleLineCommentTrivia)).ToList();
                //var Leads = prop.GetLeadingTrivia().ToList();
                //foreach (var item in Leads)
                //{
                //    sb.AppendLine($"        {item.ToString()}");
                //}

                string zhushi = string.Empty;

                if (Leads.Count > 0)
                {
                    zhushi = Leads[1].ToString().Replace("///", "");
                }

                sb.AppendLine($"        /// <summary>");
                sb.AppendLine($"        /// {zhushi}");
                sb.AppendLine($"        /// </summary>");
                #endregion

                sb.AppendLine($"public {type} {name} {{ get; set;}} ");
                sb.AppendLine();

                PropNames.Add(name);
            }

            var BaseList = classDeclarationSyntax.BaseList.Types;

            List<string> BaseOnStrings = new List<string>();

            foreach (var baseon in BaseList)
            {
                string BaseStr = (baseon.Type as IdentifierNameSyntax).Identifier.ValueText;

                if (BaseStr == "BillBaseModel")
                {
                    sb.AppendLine();
                    sb.AppendLine($"    /// <summary>");
                    sb.AppendLine($"    /// Id");
                    sb.AppendLine($"    /// </summary>");
                    sb.AppendLine("        public string BillId { get; set; }");
                    sb.AppendLine();
                    BaseOnStrings.Add("BillId");
                    sb.AppendLine();
                    sb.AppendLine($"    /// <summary>");
                    sb.AppendLine($"    /// 创建时间");
                    sb.AppendLine($"    /// </summary>");
                    sb.AppendLine("        public DateTime? CreateTime { get; set; }");
                    sb.AppendLine();
                    BaseOnStrings.Add("CreateTime");
                    sb.AppendLine();
                    sb.AppendLine($"    /// <summary>");
                    sb.AppendLine($"    /// 创建人");
                    sb.AppendLine($"    /// </summary>");
                    sb.AppendLine("        public string CreateBy { get; set; }");
                    sb.AppendLine();
                    BaseOnStrings.Add("CreateBy");
                    sb.AppendLine();
                    sb.AppendLine($"    /// <summary>");
                    sb.AppendLine($"    /// 创建人ID");
                    sb.AppendLine($"    /// </summary>");
                    sb.AppendLine("        public string CreateUserId { get; set; }");
                    sb.AppendLine();
                    BaseOnStrings.Add("CreateUserId");
                    sb.AppendLine();
                    sb.AppendLine($"    /// <summary>");
                    sb.AppendLine($"    /// 更新时间");
                    sb.AppendLine($"    /// </summary>");
                    sb.AppendLine("        public DateTime? UpdateTime { get; set; }");
                    sb.AppendLine();
                    BaseOnStrings.Add("UpdateTime");
                    sb.AppendLine();
                    sb.AppendLine($"    /// <summary>");
                    sb.AppendLine($"    /// 更新人");
                    sb.AppendLine($"    /// </summary>");
                    sb.AppendLine("        public string UpdateBy { get; set; }");
                    sb.AppendLine();
                    BaseOnStrings.Add("UpdateBy");
                    sb.AppendLine();
                    sb.AppendLine($"    /// <summary>");
                    sb.AppendLine($"    /// 更新人ID");
                    sb.AppendLine($"    /// </summary>");
                    sb.AppendLine("        public string UpdateUserId { get; set; }");
                    sb.AppendLine();
                    BaseOnStrings.Add("UpdateUserId");
                }

                if (BaseStr == "DetailBaseModel")
                {
                    sb.AppendLine();
                    sb.AppendLine($"    /// <summary>");
                    sb.AppendLine($"    /// 明细ID");
                    sb.AppendLine($"    /// </summary>");
                    sb.AppendLine("        public string DtlId { get; set; }");
                    sb.AppendLine();
                    BaseOnStrings.Add("DtlId");

                    sb.AppendLine();
                    sb.AppendLine($"    /// <summary>");
                    sb.AppendLine($"    /// 关联单据Id");
                    sb.AppendLine($"    /// </summary>");
                    sb.AppendLine("        public string BillId { get; set; }");
                    sb.AppendLine();
                    BaseOnStrings.Add("BillId");
                }
            }
            #endregion

            #region 生成转换方法
            sb.AppendLine($"        /// <summary>");
            sb.AppendLine($"        /// 将DTO转成Source类");
            sb.AppendLine($"        /// </summary>");
            sb.AppendLine($"        /// <returns>Source类</returns>");
            sb.AppendLine($"        public static {classDeclarationSyntax.Identifier.Text} FromDTO({classDeclarationSyntax.Identifier.Text}DTO dto)");
            sb.AppendLine("        {");
            sb.AppendLine($"            {classDeclarationSyntax.Identifier.Text} vo=new {classDeclarationSyntax.Identifier.Text}(); ");

            foreach (var baseon in BaseOnStrings)
            {
                sb.AppendLine($"            vo.{baseon}=dto.{baseon};");
            }

            PropNames.ForEach(f => sb.AppendLine($"vo.{f}=dto.{f};"));

            sb.AppendLine("            return vo;");

            sb.AppendLine("        }");


            sb.AppendLine($"        /// <summary>");
            sb.AppendLine($"        /// 将Source类转成DTO类");
            sb.AppendLine($"        /// </summary>");
            sb.AppendLine($"        /// <returns>DTO类</returns>");
            sb.AppendLine($"        public static {classDeclarationSyntax.Identifier.Text}DTO ToDto({classDeclarationSyntax.Identifier.Text} source)");
            sb.AppendLine("        {");
            sb.AppendLine($"            {classDeclarationSyntax.Identifier.Text}DTO dto=new {classDeclarationSyntax.Identifier.Text}DTO(); ");

            foreach (var baseon in BaseOnStrings)
            {
                sb.AppendLine($"            dto.{baseon}=source.{baseon};");
            }


            PropNames.ForEach(f => sb.AppendLine($"            dto.{f}=source.{f};"));


            sb.AppendLine("            return dto;");

            sb.AppendLine("        }");
            #endregion


            sb.AppendLine("    }");

            sb.AppendLine("}");

            string extensionTextFormatted = CSharpSyntaxTree.ParseText(sb.ToString(), new CSharpParseOptions(LanguageVersion.CSharp8)).GetRoot().NormalizeWhitespace().SyntaxTree.GetText().ToString();

            context.AddSource($"{classDeclarationSyntax.Identifier.Text}DTO.cs", SourceText.From(extensionTextFormatted, Encoding.UTF8));
        }
    }

    public void Initialize(GeneratorInitializationContext context)
    {
#if DEBUG
        //Debugger.Launch();
#endif
        context.RegisterForSyntaxNotifications(() => new DtoSyntaxReceiver());
    }
}
